package com.test.protobuf;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Strings;
import com.google.protobuf.ByteString;
import com.test.netty.nettyhb.CProtocol;
import lombok.SneakyThrows;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;

public class ProtobufDemo {

    public static void main(String[] args) throws Exception {
//        User u = new User();
//        u.setId(1000L);
//        u.setRealName("老大");
//        u.setNickName("老大");
//        u.setAge(42);
//        u.setSex(1);
//        u.setRemark("remark");
//        u.setLoginTime(new Date());
//        useJson(u);
//        useJavaSerial(u);
//        useProtobuf(u);
//        test();
//        varTest();
//        testRepeated();
//        testMap();
//        testOneof();
        testNumber();
    }


    @SneakyThrows
    public static void useJson(User u) {
        ObjectMapper mapper = new ObjectMapper();
        String str = mapper.writeValueAsString(u);
        System.out.println(str);
        System.out.println("UseJson LEN=" + str.getBytes().length);
    }

    public static void useJavaSerial(User u) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(u);
        System.out.println(new String(baos.toByteArray()));
        System.out.println("UseJavaSerial LEN=" + baos.toByteArray().length);
    }

    public static void useProtobuf(User u) throws Exception {
        Protocal.User user = Protocal.User.newBuilder()
                .setId(u.getId())
                .setRealName(u.getRealName())
                .setNickName(u.getNickName())
                .setAge(u.getAge())
                .setSex(u.getSex())
                .setLoginTime(u.getLoginTime().getTime())
                .setRemark(u.getRemark())
                .build();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        user.writeTo(baos);
        System.out.println(new String(baos.toByteArray()));
        System.out.println("UseProtobuf LEN=" + baos.toByteArray().length);

        Protocal.User userNew = Protocal.User.parseFrom(baos.toByteArray());
        System.out.println(userNew.getRealName());
    }

    /**
     * key = field << 3 | wire type
     * field = key >>> 3
     * wire type = key & 0b111
     */
    public static void protobufKeyCode() {
        int fieldNumber = 1;
        int key = fieldNumber << 3 | 5;
        System.out.println(key);
        System.out.println(Integer.toBinaryString(key));
        System.out.println(key & 0b111);
        System.out.println(key >>> 3);
    }

    public static void test() {
        int value = 0xff;
//        value >>>= 1;
        value = value >>> 7;
        System.out.println(value);
    }

    public static void varTest() throws IOException {
        Proto3Types.ProtoTypes protoTypes = Proto3Types.ProtoTypes.newBuilder()
//                .setDoubleV(3.5D)
//                .setFloatV(3.5F)
//                .setInt32V(150)
//                .setInt64V(10000L)
//                .setUint32V(150)
//                .setUint64V(10000L)
//                .setSint32V(-150)
//                .setSint64V(-10000L)
//                .setFixed32V(150)
//                .setFixed64V(10000L)
//                .setSfixed32V(-150)
//                .setSfixed64V(-10000L)
//                .setBoolV(true)
//                .setStringV("这是一次测试")
                .setBytesV(ByteString.copyFromUtf8("这也是一次测试"))
                .build();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        protoTypes.writeTo(baos);
        byte[] bytes = baos.toByteArray();
        printBytes(bytes);
        System.out.println("double=>" + Double.longBitsToDouble(0b100000000001100000000000000000000000000000000000000000000000000L));
        System.out.println("float=>" + Float.intBitsToFloat(0b1000000011000000000000000000000));
    }

    public static void testRepeated() throws IOException {
        Proto3Types.UserGroup.Builder userGroupBuilder = Proto3Types.UserGroup.newBuilder();
        for (int i = 0; i < 2; i++) {
            Proto3Types.User user = Proto3Types.User.newBuilder()
                    .setId(i + 1)
                    .setName("laoda" + i)
                    .setNumber(-11)
                    .setStatus(Proto3Types.EnumDataType.COME)
                    .build();
            userGroupBuilder.addUsers(user);
            userGroupBuilder.addDays(i);
        }
        userGroupBuilder.setId(1024L);
        Proto3Types.UserGroup userGroup = userGroupBuilder.build();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        userGroup.writeTo(baos);
        System.out.println(0b10000000000);
        System.out.println(Arrays.toString(baos.toByteArray()));
        printBytes(baos.toByteArray());
    }

    public static void testMap() throws IOException {
        Proto3Types.UserGroup.Builder builder = Proto3Types.UserGroup.newBuilder();
        for (int i = 0; i < 2; i++) {
            Proto3Types.User user = Proto3Types.User.newBuilder()
                    .setId(i + 1)
                    .setName("laoda" + i)
                    .setNumber(-11)
                    .setStatus(Proto3Types.EnumDataType.COME)
                    .build();
            builder.putClasses("c" + i, user);
        }

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        builder.build().writeTo(baos);
        System.out.println(Arrays.toString(baos.toByteArray()));
        printBytes(baos.toByteArray());
    }

    public static void testOneof() throws IOException {
        CProtocol.Request req = CProtocol.Request.newBuilder()
                .setStatus(1)
                .setMsg("A")
                .setData("A")
                .build();

        CProtocol.Response res = CProtocol.Response.newBuilder()
                .setStatus(1)
                .setMsg("B")
                .setData("B")
                .build();
        CProtocol.Message message = CProtocol.Message.newBuilder()
                .setRequest(req)
                .build();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        message.writeTo(baos);
        System.out.println(Arrays.toString(baos.toByteArray()));
        printBytes(baos.toByteArray());
        if (message.hasRequest()) {
            System.out.println("req");
        } else {
            System.out.println("res");
        }
    }

    public static void testNumber() {
        System.out.println(Strings.padStart(Integer.toBinaryString(88 & 0xff), 8, '0'));
    }

    public static void printBytes(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        sb.append("[\n");
        int i = 0;
        for (byte b : bytes) {
            sb.append(Strings.padStart(Integer.toBinaryString(b & 0xff), 8, '0'));
            if (i != bytes.length - 1) {
                sb.append(", ");
            }
            if ((i + 1) % 8 == 0) {
                sb.append("\n");
            }
            i++;
        }
        sb.append("\n]");
        System.out.println(sb.toString());
    }


}
